//
//  DerbyNSOdeResolver.m
//  DerbyOSSampler
//
//  Created by glenn andreas on 1/6/14.
//  Copyright (c) 2014 glenn andreas. All rights reserved.
//

#import "DerbyNSOdeResolver.h"
#import "DerbyNSOdeLog.h"
@interface DerbyNSOdeResolver()
@property (strong) NSNetServiceBrowser *netDomainBrowser;
@property (strong) NSMutableArray *netServiceBrowsers;
@property (strong) NSMutableArray *waitingToResolve;
@property (strong) NSNetService *netService;
@property (weak) id<DerbyNSOdeResolverClient> delegate;
@property (strong) NSString *regType;
@property (strong) NSString *boutId;
@end

@implementation DerbyNSOdeResolver
- (id) initWithRegistrationType: (NSString *) regType delegate: (id<DerbyNSOdeResolverClient>) delegate
{
    self = [super init];
    if (self) {
        _delegate = delegate;
        _regType = regType;
    }
    return self;
}
- (void) broadcastIdentity: (NSString *) name port: (NSInteger) port uuid: (NSString *) uuid boutId: (NSString *) boutId
{
    self.boutId = boutId;

    self.netDomainBrowser = [[NSNetServiceBrowser alloc] init];
    self.netDomainBrowser.delegate = self;
    [self.netDomainBrowser scheduleInRunLoop: [NSRunLoop mainRunLoop] forMode:NSRunLoopCommonModes];
    [self.netDomainBrowser searchForBrowsableDomains];
    
    NSMutableDictionary *txtDict = [NSMutableDictionary dictionary];
    if (uuid) {
        [txtDict setObject:uuid forKey:@"uuid"];
    }
    if (boutId) {
        [txtDict setObject:boutId forKey:@"bout"];
    }
    self.netService = [[NSNetService alloc] initWithDomain:@"" type:self.regType name:name port: port];
    self.netService.delegate = self;
    [self.netService setTXTRecordData:[NSNetService dataFromTXTRecordDictionary:txtDict]];
    [self.netService publish];
}

- (void)netServiceBrowser:(NSNetServiceBrowser *)aNetServiceBrowser didFindDomain:(NSString *)domainString moreComing:(BOOL)moreComing
{
    // start browsing for services here
    NSNetServiceBrowser *netServiceBrowser = [[NSNetServiceBrowser alloc] init];
    netServiceBrowser.delegate = self;
    [netServiceBrowser scheduleInRunLoop: [NSRunLoop mainRunLoop] forMode:NSRunLoopCommonModes];
    [netServiceBrowser searchForServicesOfType:self.regType inDomain:domainString];
    if (!self.netServiceBrowsers) self.netServiceBrowsers = [NSMutableArray array];
    [self.netServiceBrowsers addObject:netServiceBrowser];
}
- (void)netServiceBrowser:(NSNetServiceBrowser *)aNetServiceBrowser didNotSearch:(NSDictionary *)errorDict;
{
    LOG_ERR(DNS, @"Failed to search domain %@", errorDict);
}
- (void) notifyDelegateAboutService: (NSNetService *) aNetService
{
    NSData *txtRecord = [self.netService TXTRecordData];
    NSDictionary *dict = [NSNetService dictionaryFromTXTRecordData:txtRecord];
    NSString *boutId = [[NSString alloc] initWithData:[dict objectForKey:@"bout" ] encoding:NSUTF8StringEncoding];
    NSString *uuid = [[NSString alloc] initWithData:[dict objectForKey:@"uuid"] encoding:NSUTF8StringEncoding];
    // only bother with bouts that are the same as our bout (or nil if ours is nil
    if (self.boutId) {
        if ([self.boutId isEqualToString:boutId])
            [self.delegate addPeer: aNetService uuid: uuid];
    } else if (!boutId) {
        [self.delegate addPeer: aNetService uuid: uuid];
    }
}
- (void)netServiceDidResolveAddress:(NSNetService *)sender
{
    LOG_DBG(DNS,@"Resolved %@: %@", sender, sender.addresses);
    [self notifyDelegateAboutService:sender];
    [self.waitingToResolve removeObject: sender];
}
- (void)netService:(NSNetService *)sender didNotResolve:(NSDictionary *)errorDict;
{
    LOG_ERR(DNS,@"Couldn't resolve %@: %@", sender, errorDict);
    [self.waitingToResolve removeObject: sender];
}
- (void)netServiceBrowser:(NSNetServiceBrowser *)aNetServiceBrowser didFindService:(NSNetService *)aNetService moreComing:(BOOL)moreComing;
{
    if ([aNetService.type isEqualToString:self.regType]) {
        if (aNetService.addresses.count == 0) {
            // no addresses, so resolve
            LOG_INFO(MDNS,@"Need to resolve address for %@",aNetService);
            if (!self.waitingToResolve) self.waitingToResolve = [NSMutableArray array];
            NSNetService *service = [[NSNetService alloc] initWithDomain:aNetService.domain type:aNetService.type name:aNetService.name];
            [service scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSRunLoopCommonModes];
            service.delegate = self;
            [service resolveWithTimeout:5];
            [self.waitingToResolve addObject:service];
            return;
        } else {
            [self notifyDelegateAboutService:aNetService]; // already has addresses
        }
    }
}

- (void)netServiceBrowser:(NSNetServiceBrowser *)aNetServiceBrowser didRemoveService:(NSNetService *)aNetService moreComing:(BOOL)moreComing
{
    if ([aNetService.type isEqualToString:self.regType]) {
        NSData *txtRecord = [self.netService TXTRecordData];
        NSDictionary *dict = [NSNetService dictionaryFromTXTRecordData:txtRecord];
        NSString *boutId = [[NSString alloc] initWithData:[dict objectForKey:@"bout" ] encoding:NSUTF8StringEncoding];
        NSString *uuid = [[NSString alloc] initWithData:[dict objectForKey:@"uuid"] encoding:NSUTF8StringEncoding];
        // only bother with bouts that are the same as our bout (or nil if ours is nil
        if (self.boutId) {
            if ([self.boutId isEqualToString:boutId])
                [self.delegate removePeer: aNetService uuid: uuid];
        } else if (!boutId) {
            [self.delegate removePeer: aNetService uuid: uuid];
        }
    }
}

@end
